






import { Button } from "antd"
import { Outlet } from "react-router-dom"
export default () => {
  const ObjExpre1 = () => {
    //对象方法：
    Object.prototype.obj_fun = function () { console.log('object function') }

    //类方法：
    Object.class_fun = function () { console.log('class function') }

    //也是类方法
    function Class_cus() {

    }
    Class_cus.class_fun2 = function () { console.log('Class_cus function') }
    Class_cus.class_fun2()  //Class_cus function

    //也是对象方法
    Class_cus.prototype.obj_fun2 = function () { console.log('Class_cus_obj function ') }
    var cc = new Class_cus()
    cc.obj_fun2()

    Object.class_fun()
    let a = new Object()
    a.obj_fun()




    //删除方法
    delete Object.prototype.obj_fun

    //总结一：
    //---  方法：可以动态add,delete 类或对象方法,  prototype可以理解成对象的 本质，又可以理解成继承哪个对象
    //--- 普通静态方法有哪些:keys，OwnPropertyNames
    //类方法:构造函数.f1，对象方法：构造函数.prototype.f1

  }

  const ObjExpre2 = () => {
    var n = Object(3)
    console.log(n instanceof Number)


    var o = new Object()
    console.log(o)

    //总结二：Object()，功能1：包装成对象 功能2：构造函数(+new)
  }

  const ObjExpre3 = () => {
    //get对属性的描述：返回一个对象,修改对象无用
    var go = { p: '3' }
    let p = Object.getOwnPropertyDescriptor(go, 'p')//不能用于继承的属性
    console.log(p)

    //modify or add 对象属性
    console.log(Object.keys(go))
    var go_modify = Object.defineProperty(go, 'p', {
      enumerable: false
    })
    console.log(Object.keys(go_modify))
    console.log(Object.getOwnPropertyNames(go_modify))

    //属性是否可遍历
    console.log(go_modify.propertyIsEnumerable())

    //
    go_modify = Object.defineProperty(go_modify, 'x', {
      value: 'hello',
      enumerable: false,
      configurable: false
    })

    try {
      Object.defineProperty(go_modify, 'x', {
        configurable: true
      })
    } catch (e) {
      console.log(e)
    }

    go_modify = Object.defineProperty(go_modify, 'y', {
      set: function (param) {
        this.p = param
        console.log(param)
      },
      get: function () {
        return p
      }
    })
    go_modify.y = 789
    console.log(go_modify)





    //总结三：
    //--- getOwnPropertyDescriptor获取不到继承的,修改无用，得用defineproperty
    //--- getOwnPropertyNames，包括不可遍历
    //--- set,get 和value  writeable冲突
    //--- defineProperty:修改属性描述
    //--- configurable:只要配置成false，后续哪怕再配置成yes都不允许,理解成焊死了
    //--- 只有构造函数有原型，且原型必须是对象，对象是没有原型的:JavaScript 规定，每个函数都有一个prototype属性，指向一个对象。


  }

  const ObjExpre4 = () => {
    var valOf = new Object()
    valOf.valueOf = function () {
      return 3
    }
    console.log(1 + valOf)//4
    console.log(1 - valOf)//4


    var toStr = new Object()
    toStr.toString = function () {
      return "hhhh"
    }
    console.log(toStr.toString())

    //数据类型
    console.log(Object.prototype.toString.call(toStr))
    console.log(Object.prototype.toString.call(2))

    //typeof 不准确,用toString

    //获取精确类型
    function type(o) {
      var s = Object.prototype.toString.call(o)
      return s.match(/\[object (.*?)\]/)[1].toLowerCase();
    }
    //给type方法，再添加内部方法 isNumber等
    ['Null',
      'Undefined',
      'Object',
      'Array',
      'String',
      'Number',
      'Boolean',
      'Function',
      'RegExp'
    ].forEach(function (t) {
      type['is' + t] = function (o) {
        return type(o) === t.toLowerCase();
      };
    });

    console.log(type.isNumber(3))

    try {
      var obj_t = new type()
      console.log(obj_t.isNumber(4))//error
    } catch (e) {
      console.log(e)
    }


    //获取对象的原型：获取class
    Object.getPrototypeOf(valOf)

    //设置对象的原型:对象没有prototype，不能prototype = xxx
    Object.setPrototypeOf(valOf,Object.prototype)

    //没有构造函数，但想创造同类别实例
    let valCopy = Object.create(valOf)

    //但是对象有__proto__,也是返回原型
    function _proto() {

    }
    function _proto2() {

    }
    var _p = new _proto()
    _p.__proto__ = _proto2.prototype
    
    console.log(_p instanceof _proto)
    console.log(_p instanceof _proto2)

    //forin 不去分是不是自身的。
    //获取所有属性:一层一层原型链条读取
    function getAll(cls) {
      var properties = {}
      while(cls) {
        Object.getOwnPropertyNames(cls).forEach(p=>{properties[p]=true})
        cls = Object.getPrototypeOf(cls)
      }
      return properties
    }
    console.log(getAll(Array))

    //copy
    var date = new Date()
    
    function Copy(obj) {
      var copy_obj = Object.create(Object.getPrototypeOf(obj))//原型复制过来

      //属性值复制过来
      copyProperites(copy_obj,obj)
      return copy_obj

    }
    function copyProperites(target,origin) {
      Object.getOwnPropertyNames(origin)
      .forEach(propkey => {
        var desc = Object.getOwnPropertyDescriptor(origin,propkey)
        Object.defineProperty(target,propkey,desc)
      })
      return target
    }
    console.log(date)
    console.log(Copy(date))

    //总结三：
    //valueof，在运算的时候用到，比如 + ,-,if,默认返回自己
    //toString,默认返回[object object],可以判断数据类型（原理：调用最父类的方法，也就是最原始定义时候的方法）
    //

    //hasOwnProperty

  }
  const ObjExpre5 = () => {
    //对象创建
    function ForceNew(param){
      if(!(this instanceof ForceNew)) {
        return new ForceNew(param)
      }
      
      this._p = param//可行的，这是一个构造函数，不存在this问题，如果对象的方法使用this，就会有问题
    }
    
    var f =  ForceNew('hello')
    console.log(f._p)

    function simlutorNew(constructor_s,params) {
      var args = [].slice.call(params)

      var constructor = args.shift()
      //以上 获取参数和构造函数

      var context = Object.create(constructor.prototype) //空对象，并且设定好原型：有了一个雏形:context的原型 === constructor的原型

      var result = constructor.apply(context,args)  //构造方法初始化对象

      return context
      //简述：提取材料->打出雏形->精细化雕

    }

    //原型目的：共享方法和属性值，
    //构造函数才有 prototype，对象没有prototype,对象有constructor：Fun.prototype.constructor === obj.constructor
    //Dog的原型最终指向 Object.原型，Object.原型的原型 是 null，没有意义
    //方法调用跟OC一样

    //所以引申一个问题，constructor在prototype上，prototype变了，constructor要同步过去



    //构造函数 跟constructor关系:a->b->c->a
    // function Person{
    //   _prototype: {
    //     constructor:Person//循环引用
    //   }
    // }

    //_instanceOf 
    //--- 唯一失真，Object.create(null)
    //--- 只能判断对象，字符串不是对象，所以 '' instanceof string 是false
    function _instanceOf(o,f) {

      var proto = f.prototype
      var o_proto = Object.getPrototypeOf(o)
      var ret = false
      while(o_proto) {
        console.log(proto,o_proto)
        if (proto === o_proto) {
          
          ret = true
          break
        }
        o_proto =Object.getPrototypeOf(o_proto)
        console.log(proto,o_proto)
      }
      return ret
      // return f.prototype.isPrototypeOf(o)//f是不是o的原型,应该递归找
    }
    function FunSuper() {
      this.x = 3
    }
    function Fun(){
      FunSuper.call(this)
    }
    Fun.prototype = Object.create(FunSuper.prototype)
    Fun.prototype.constructor = Fun
    var f = new Fun()
    console.log(_instanceOf(f,FunSuper))//true


    //继承
    function Super(param) {
      this.param = param
    }
    function Sub(param) {
      Super.call(this)
      this._param = param
    }

    //--- 第一种:设置prototype、constructor 特点： 子类原型修改，不影响父类
    Sub.prototype = Object.create(Super.prototype)
    Sub.prototype.constructor = Sub

    //--- 第二种 原型有了父类的实例方法，不推荐
    // Sub.prototype = new Super()

    //多继承
    function M1() {

    }
    function M2() {
      
    }
    function S() {
      M1.call(this)
      M2.call(this)
    }
    //同上：设置prototype、constructor
    S.prototype = Object.create(M1.prototype)
    Object.assign(S.prototype,M2.prototype)//合并原型

    S.prototype.constructor = S








    //错误示范，对象没有原型,只有函数有原型，且原型必须是对象
    var obj = { p1: 123, p2: 456 }
    var obj_child = { p3: 789 }
    obj_child.prototype = obj

    //正确示范
    function Animal() {
      this.p1 = 123
      this.p2 = 456
    }
    function Person() {
      this.p3 = 789
    }
    Person.prototype = new Animal()

    var anl = new Animal()
    var per = new Person()

    console.log(anl)
    console.log(per)

    console.log(Object.keys(anl))//[p1,p2]
    console.log(Object.keys(per))//[p3]

    console.log(Object.getOwnPropertyNames(anl))//[p1,p2]
    console.log(Object.getOwnPropertyNames(per))//[p3]

    //总结一：
    //构造函数没有this问题，对象的方法里this有可能有问题
    //Object.create():没有构造方法，我就从一个对象copy一个。
    //this就是属性或方法,“当前”所在的对象。也就是谁调用就是谁
    //总结三：
    //--- this: 谁调用this所在的方法，this就指向谁
    //--- this的问题，就是要冰冻this的指向：1.构造函数不存在this问题，2.对象方法的this指向对象，但可以改变3.
    //-- 冰冻this：方法.1. 方法.call(对象),弊端：没办法传参,优势：简化调用
    //---构造函数问题：每个对象都有一个方法，造成内存的浪费。怎么办，共享方法呗
    //---//原型目的：共享方法和属性值
    //---isPrototypeOf 是原型链条比较
  }

  const ObjExpre6 = () => {
    //第一阶段:对象封装, 缺点太暴露，并且容易被改
    var module1 = {
      _count:0,
      m1_1:function(){console.log(this._count,'module1 m1_1')},
      m1_2:function(){}
    }
    module1.m1_1()
    //第二阶段:IIFE  本质是执行过后，构造函数内部局部变量就消失了，但返回的对象的方法，还引用着局部变量。变相没有消失
    var module2 = (function(){
      var _count = 88
      var m2_1 = function(){
        console.log('module1 m1_1',_count)
      }
      return {m1:m2_1}
    })()
    module2.m1()
    console.log(module2._count)//undefined

    //第二阶段： 模块拆分 本质：可以不断地给原始对象添加新东西。
    var module3 = (function(mod){
      mod.m3_1 = function(){}
    })(window.module3 || {})

  }
//typeof 和 instanceof https://juejin.cn/post/7042164762456621087
  return (
    <div>
      <Button type="primary" onClick={ObjExpre1}>类、对象的方法</Button>
      <Button type="primary" onClick={ObjExpre2}>Object及其方法</Button>
      <Button type="primary" onClick={ObjExpre3}>属性</Button>
      <Button type="primary" onClick={ObjExpre4}>Object原型方法</Button>
      <Button type="primary" onClick={ObjExpre5}>继承</Button>
      <Button type="primary" onClick={ObjExpre6}>模块</Button>
      <Outlet></Outlet>
    </div>
  )
}